package com.icesoft.core.dao.criteria;

import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.Entity;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


public class FromBuilder implements Cloneable {
    int subOrder = 0;
    private Map<Class<?>, String> tables = new LinkedHashMap<>(5);
    boolean distinct = false;

    @Getter
    boolean skipFilter = false;

    public FromBuilder skipFilter() {
        skipFilter = true;
        return this;
    }

    Map<Class<?>, String> getTableMap() {
        return tables;
    }

    public static FromBuilder get() {
        // 执行sql前调用setFromTable替换
        return get(Object.class);
    }

    public static FromBuilder get(Class<?> formClazz) {
        return get(formClazz, "f");
    }

    public static FromBuilder get(Class<?> formClazz, String name) {
        return new FromBuilder(formClazz, name);
    }

    public void setFromTable(Class<?> fromTable) {
        this.fromTable = fromTable;
        tables.put(this.fromTable, tableName);
    }

    public FromBuilder distinct() {
        distinct = true;
        return this;
    }

    @Getter
    Class<?> fromTable;
    private String tableName;

    public String getTableName() {
        if (subOrder > 0) {
            return tableName + subOrder;
        }
        return tableName;
    }

    public FromBuilder(Class<?> formClazz, String name) {
        this.fromTable = formClazz;
        this.tableName = name;
        if (formClazz != null && formClazz != Object.class) {
            tables.put(formClazz, name);
        }
        limitBuilder = new LimitBuilder(this);
    }

    public TableOnBuilder leftJoin(Class<?> joinClazz, String name) {
        return join(joinClazz, name, true);
    }

    public TableOnBuilder join(Class<?> joinClazz, String name) {
        return join(joinClazz, name, false);
    }

    private List<TableOnBuilder> joins = new ArrayList<>(5);

    TableOnBuilder join(Class<?> joinClazz, String nickname, boolean left) {
        if (tables.containsValue(nickname)) {
            throw new IllegalArgumentException("别名重复：" + nickname);
        }
        if (tables.containsKey(joinClazz)) {
            throw new IllegalArgumentException("已添加过该表");
        }
        tables.put(joinClazz, nickname);
        TableOnBuilder tableOnBuilder = new TableOnBuilder(joinClazz, nickname, left);
        joins.add(tableOnBuilder);
        return tableOnBuilder;
    }

    @Override
    public FromBuilder clone() {
        FromBuilder fromBuilder = new FromBuilder(subOrder, new LinkedHashMap<>(tables), distinct, fromTable, tableName, new ArrayList<>(joins));
        if (this.whereBuilder != null) {
            fromBuilder.whereBuilder = this.whereBuilder.copy(fromBuilder);
        }
        if (this.orderBuilder != null) {
            fromBuilder.orderBuilder = this.orderBuilder.copy(fromBuilder);
        }
        if (this.groupByBuilder != null) {
            fromBuilder.groupByBuilder = this.groupByBuilder.copy(fromBuilder);
        }

        fromBuilder.limitBuilder = this.limitBuilder.copy(fromBuilder);
        return fromBuilder;
    }

    WhereBuilder whereBuilder;
    OrderBuilder orderBuilder;
    GroupByBuilder groupByBuilder;
    LimitBuilder limitBuilder;

    private FromBuilder(int subOrder, Map<Class<?>, String> tables, boolean distinct, Class<?> fromTable, String tableName, List<TableOnBuilder> joins) {
        this.subOrder = subOrder;
        this.tables = tables;
        this.distinct = distinct;
        this.fromTable = fromTable;
        this.tableName = tableName;
        this.joins = joins;
    }

    public PropertyFilter where() {
        if (whereBuilder != null) {
            return whereBuilder.currentFilter;
        }
        whereBuilder = new WhereBuilder(this);
        return whereBuilder.currentFilter;
    }

    public OrderBuilder order() {
        if (orderBuilder == null) {
            orderBuilder = new OrderBuilder(this);
        }
        return orderBuilder;
    }

    public GroupByBuilder group() {
        if (groupByBuilder == null) {
            groupByBuilder = new GroupByBuilder(this);
        }
        return groupByBuilder;
    }

    public FromBuilder page(int page, int size) {
        limitBuilder = new LimitBuilder(this).page(page, size);
        return this;
    }

    public FromBuilder offsetLimit(int offset, int limit) {
        limitBuilder.offset(offset).limit(limit);
        return this;
    }

    public int getOffset() {
        return limitBuilder.getOffset();
    }

    public int getLimit() {
        return limitBuilder.getLimit();
    }

    @Override
    public String toString() {
        return getFromHql();
    }

    public String getFromHql() {
        StringBuilder stringBuilder = new StringBuilder();
        for (TableOnBuilder table : joins) {
            stringBuilder.append(table.toString());
        }
        return "from " + getEntityName(fromTable) + " " + getTableName() + stringBuilder.toString();
    }

    String appendNickName(String propertyName) {
        if (!"*".equals(propertyName) && !propertyName.contains(".")
                && !StringUtils.isNumeric(propertyName.substring(0, 1))) {
            propertyName = getTableName() + "." + propertyName;
        }
        return propertyName;
    }

    /**
     * 获取实体Entity的名称
     *
     * @param clazz 带Entity注解的类
     * @return entity.name()或clazz.getSimpleName()
     */
    public static String getEntityName(Class<?> clazz) {
        String entityname = clazz.getSimpleName();

        Entity entity = clazz.getAnnotation(Entity.class);
        if (entity != null && !"".equals(entity.name())) {
            entityname = entity.name();
        }
        return entityname;
    }

    public class TableOnBuilder {
        Class joinClazz;
        String nickName;
        boolean left;
        String joinTableColumn;
        String otherColumn;

        public TableOnBuilder(Class joinClazz, String nickName, boolean left) {
            this.joinClazz = joinClazz;
            this.nickName = nickName;
            this.left = left;
        }

        public FromBuilder on(String joinTableColumn, String otherColumn) {
            this.joinTableColumn = joinTableColumn;
            this.otherColumn = otherColumn;
            return FromBuilder.this;
        }

        @Override
        public String toString() {
            StringBuilder stringBuilder = new StringBuilder();
            if (left) {
                stringBuilder.append(" left");
            }
            stringBuilder.append(" join ").append(getEntityName(joinClazz)).append(" ").append(nickName);
            if (joinTableColumn != null) {
                stringBuilder.append(" on ");
                if (!joinTableColumn.contains(".")) {
                    stringBuilder.append(nickName).append(".");
                }
                stringBuilder.append(joinTableColumn).append(" = ");
                if (!otherColumn.contains(".")) {
                    stringBuilder.append(getTableName()).append(".");
                }
                stringBuilder.append(otherColumn);
            }
            return stringBuilder.toString();
        }
    }

}
